#ifndef MODEL
#define MODEL

#include <pthread.h>
#include <semaphore.h>

#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <ctime>

#include <queue>

#define TIME_RATIO 1

class Client {
    public:
        Client(const int e_time, const int s_time, const int id): \
            enter_time(e_time), serve_time(s_time), queue_num(0), id(id) {}
        Client(): enter_time(0), serve_time(0), queue_num(0), id(0) {}

        void be_served() {
            sleep(serve_time*TIME_RATIO);
        }

        // data accesss control
        int get_enter_time() {
            return enter_time;
        }

        int get_serve_time() {
            return serve_time;
        }

        int get_id() {
            return id;
        }

        // data setting control
        void set_queue_num(int queue_num) {
            this->queue_num = queue_num;
        }

        void set_serve_start_time(time_t t) {
            start_serve_time = t;
        }

        void set_leave_time(time_t t) {
            leave_time = t;
        }

        void set_serve_worker_id(int _id) {
            serve_worker_id = _id;
        }

        void set_tot_start_time(time_t t) {
            tot_start_time = t;
        }

        // debug
        void print_queue_info() {
            printf("%d\t\t%d\t\t%s\t\t%s\t\tGet Queue number %d\t\t\n", id, enter_time, "--", "--", queue_num);
        }

        void debug_print() {
            printf("id: %d, enter_time: %d, server_time: %d\n", id, enter_time, serve_time);
        }

        void debug_serve_info(int worker_id) {
            printf("%d\t\t%d\t\t%ld\t\t%s\t\tWorker:%d Start Serving\t\t\n", id, enter_time, start_serve_time-tot_start_time,
                    "--", serve_worker_id);
        }

        void debug_leave_info() {
            printf("%d\t\t%d\t\t%ld\t\t%ld\t\tLeave Bank\t\t\n", id, enter_time, start_serve_time-tot_start_time,
                    leave_time-tot_start_time);
        }

        void print_event(time_t start_time) {
            printf("Client: %d, serve_worker_id: %d, enter_time: %d, required serve_time: %d, queue_num: %d, start serve time: %ld, leave_time: %ld\n", \
                    id, serve_worker_id, enter_time, serve_time, queue_num, start_serve_time-start_time, leave_time-start_time);
        }

    private:
        int enter_time = 0; // time for entering bank
        int serve_time = 0; // time requiring for serving
        int queue_num = 0;  // queue num got
        int id = 0;         // client id
        int serve_worker_id = 0; // worker id of serving client

        // acutal timestamp
        time_t tot_start_time;
        time_t start_serve_time;
        time_t leave_time;
};

struct ClientQueueData {
    std::queue<Client*>* waiting_queue;
    std::queue<Client*>* client_queue;
    time_t* start_time;
    sem_t* sem;
    pthread_mutex_t* mutex;
};

struct QueueInfo {
    // Storing info for getting queue_number
    std::queue<Client*>* waiting_queue;
    Client* client;
    int* queue_num;
    pthread_mutex_t* mutex;
    sem_t* sem;
};

struct WorkerInfo {
    std::queue<Client*>* waiting_queue;
    std::queue<Client*>* clients;
    int worker_id;
    pthread_mutex_t* mutex;
    sem_t* sem;
};

#endif